PDO具備了幾個特性,除了是不同資料庫例如MySQL、SQLite、SQL Server等的抽象層,他提供的Prepared Statement也可以為資料庫操作增加一些安全性與執行效率。既然有這樣的好處,就把DAO實作轉換到PDO試試看。
參考:http://us1.php.net/manual/en/book.pdo.php
不過更換不同的實作,就需要在bootstrap.php中進行不同的初始化作業,既然在設定檔中已經訂好了指定不同DAO實作的參數,那利用這個參數,也可以指定在bootstrap中做不同的初始化過程。
稍微改了一下,發現如果定義好了各個實作的bootstrap方式,其實就可以把建立連線的部份從bootstrap.php中拿掉,做成一個singleton,需要取得連線的時候就呼叫。
調整過的bootstrap.php就直接刪掉資料庫連線的程式碼:
<?php
session_start();
include 'config.php';
//register autoloader
include 'vendor/autoload.php';
set_include_path(get_include_path().PATH_SEPARATOR.CLASS_DIR);
spl_autoload_extensions(".php");
spl_autoload_register();
spl_autoload_register(function($class) {
$class = str_replace('_', '/', $class);
$class = str_replace('\\', '/', $class);
include $class.".php";
}, false);
然後設計一個MysqlBootstrap類別:
<?php
namespace Fillano\Core;
class MysqlBootstrap
{
private static $conn = null;
private function __construct()
{
}
public static function getResource() {
if(self::$conn===null) {
self::$conn = mysql_connect(DB_HOST, DB_USER, DB_PASSWORD);
if(!self::$conn)
throw new \Exception('mysql connection error: '.mysql_error());
mysql_select_db(DB_NAME);
}
return self::$conn;
}
}
然後在MysqlModelFactory中呼叫他:
<?php
namespace Fillano\Core;
class MysqlModelFactory
{
public static function getInstance($name) {
if(!defined('MODEL_IMPL')) {
die('Model Implementation constant "MODEL_IMPL" not defined in config.php');
}
$class = 'Fillano\\Models\\'.MODEL_IMPL.$name;
$bootstrap = 'Fillano\\Core\\'.MODEL_IMPL.'Bootstrap';
if(class_exists($class) && class_exists($bootstrap)) {
$conn = $bootstrap::getResource();
return new $class($conn);
} else {
throw new \Exception("class $class or $bootstrap not found.");
}
}
}
修改結束。回頭看一下是否可以執行...改了幾個小錯誤就沒問題了(我搞錯self的寫法)。
接下來,就來寫用PDO實作的DAO。在這之前,跟之前Mysql的實作一樣,需要準備好MODEL_IMPL參數,就從"Mysql"改成"PDO"吧。定好名字,就需要寫幾個類別:PDOBootstrap、PDOModelFactory以及以PDO為前綴的DAO類別。
先來寫PDOBootstrap,同樣在這個類別用singleton的方式建立PDO物件:
<?php
namespace Fillano\Core;
class PDOBootstrap
{
private static $pdo = null;
private function __construct() {}
public static function getPDO()
{
if(self::$pdo===null) {
self::$pdo = new \PDO("mysql:host=".DB_HOST.";dbname=".DB_NAME, DB_USER, DB_PASSWORD);
}
return self::$pdo;
}
}
然後是PDOModelFactory,利用他把pdo物件傳給各個DAO:
<?php
namespace Fillano\Core;
class PDOModelFactory
{
public static function getInstance($name) {
if(!defined('MODEL_IMPL')) {
die('Model Implementation constant "MODEL_IMPL" not defined in config.php');
}
$class = 'Fillano\\Models\\'.MODEL_IMPL.$name;
$bootstrap = 'Fillano\\Core\\'.MODEL_IMPL.'Bootstrap';
if(class_exists($class) && class_exists($bootstrap)) {
return new $class($bootstrap::getPDO());
} else {
throw new \Exception("class $class or $bootstrap not found.");
}
}
}
最後來寫用PDO實作的DAO,之前用Mysql實作的叫做MysqlIndex,現在改成PDOIndex:
<?php
namespace Fillano\Models;
class PDOIndex implements IIndex
{
private $pdo;
public function __construct($pdo)
{
$this->pdo = $pdo;
}
public function getForumList()
{
$sql = "SELECT f.*,count(a.forums_id) AS count "
."FROM forums f "
."LEFT JOIN articles a ON a.forums_id = f.id "
."GROUP BY a.forums_id "
."ORDER BY f.id"
;
$forums = $this->pdo->query($sql);
$data = array();
$sql = "SELECT * "
."FROM articles "
."WHERE forums_id=:forum_id "
." ORDER BY id DESC LIMIT 1";
$stmt = $this->pdo->prepare($sql);
foreach($forums as $forum) {
$article = $stmt->execute(array(":forum_id"=>$forum['id']));
$data[] = array('id'=>$forum['id'], 'name'=>$forum['name'], 'count'=>$forum['count'], 'title'=>$article['title']);
}
return $data;
}
}
寫好以後,回去改設定檔config.php,把實作從Mysql改成PDO:
<?php
define('DB_HOST','localhost');
define('DB_USER','root');
define('DB_PASSWORD', '');
define('DB_NAME', 'myforum');
define('CLASS_DIR', 'class/');
define('TEMPLATE_ENGINE', 'TwigView');
define('MODEL_IMPL', 'PDO');
打開瀏覽器操作一下看看...嗯,跟之前一樣,應該沒有問題。
======
抽換DAO實作的過程,完全沒有碰到主程式,也沒動到View與樣板,這就是把商業邏輯從主程式分離開來的好處。另外,有了這些實作,終於可以做單元測試了,明天就來針對Index這個Model來做單元測試吧。(本來還想找一個ORM來做DAO的實作,不過我怕沒時間了,所以先寫測試,後面幾天留給Controller)
PHP已經有很多成熟的資料庫抽象層與ORM,如果要從mysql/mysqli開始,最接近的大概是PDO,接下來可以嘗試一些query builder,例如FluentPDO、PHP SqlBuilder(國產)等方案,最後還有完整的ORM,例如Doctrine、Propel等等。
PHP如果要支援PDO
要在安裝PHP時就啟用它
不然早期的PHP版本記得是預設不啟用PDO
現今的PHP未知是否預設啟用PDO
PDO從PHP-5.0加入PECL,PHP-5.1內建在PHP extension(也就是原始檔的ext目錄),不過預設是否啟用,恐怕跟套件發行者有關。也就是說,這要看AppServ、WAMP、XAMPP等發行者,或是像rpm/deb等系統的套件管理者,是否有把它在編譯及發行時設為預設套件。
Dear all,
或者,也可以使用 RedBean 之類的 ORB,可以抽象畫資料庫,他是架構在 PDO 之上,所以還是與發行套件有關。
我目前使用的 ZurmoCRM 就以 RedBean 作為 ORB,簡化來回修改資料結構的時間。
Just my two cents.
Best regards,
Amigo